package ngram_reader.viewer.query;

import function.maker.UnaryFunctionMaker;
import function.unary.UnaryFunction;
import math.mathExtended.MathExtended;
import ngram.util.IOUtil;
import stringFunctions.StringFunctions;
import stringFunctions.Stringify;

/**
 *
 * @author Shimu
 * @date 7-Jul-2013
 */
public class NoStatSearchQuery implements NGramSearchQuery{

    public static enum Match {

        /** The query can exist anywhere in the n-gram */
        ANYWHERE("Anywhere"),
        /** The query must match the n-gram exactly. */
        EXACT("Exact Match"),
        /** The query must be at the beginning of the n-gram. */
        PREFIX("Prefix"),
        /** The query must be at the end of the n-gram. */
        SUFFIX("Suffix");
        private final String DISPLAY_NAME;

        private Match(String displayName) {
            this.DISPLAY_NAME = displayName;
        }

        @Override
        public String toString() {
            return DISPLAY_NAME;
        }
    }
    private String query;
    private Match match;
    protected int startGram, endGram;
    private UnaryFunction<String, Boolean> isTarget;
    private Accumulator yearMatchVolumeAc;

    protected NoStatSearchQuery(String query, Match match, int startGram, 
            int endGram, boolean matchCase) {
        this.query = query;

        this.startGram = startGram;
        this.endGram = endGram;

        this.match = match;
        this.isTarget = getTargetChecker(matchCase);
        this.yearMatchVolumeAc = new Accumulator() {

            @Override
            public void reset() {
            }

            @Override
            public void accumulate(int year, long match, long volume) {
            }

            @Override
            public String getAccumulatedValue() {
                return "";
            }
        };
    }

    public boolean ignoreStatistics() {
        return true;
    }
    
    public boolean withinGram(int gram) {
        return MathExtended.bounded(gram, startGram, endGram);
    }

    public UnaryFunction<String, Boolean> getIsTarget() {
        return isTarget;
    }

    public Accumulator getAccumulator() {
        return this.yearMatchVolumeAc;
    }

    /**
     * Generates a function that returns true when a given n-gram matches
     * the search term based on certain criteria specified by the user.
     * For example if the user selected the match case check box and
     * the prefix radio button, then the returned UnaryFunction will return
     * true if and only if a given word begins with the given searchTerm,
     * case matters.
     * 
     * @param matchCase true if case matters, false otherwise
     * @param anyWhere true if the query can be anywhere in the n-gram, false
     * otherwise.
     * @return 
     */
    private UnaryFunction<String, Boolean> getTargetChecker(
            boolean matchCase) {

        // Converts the searchTerm into the encoding used by NGramProcessor
        byte[] bytes = this.query.getBytes(IOUtil.ENCODING);
        this.query = new String(bytes, IOUtil.ENCODING);

        this.query = matchCase ? query : query.toLowerCase();

        final UnaryFunction<String, Boolean> returnVal;

        switch (match) {
            case ANYWHERE:
                returnVal = new UnaryFunction<String, Boolean>() {

                    @Override
                    public Boolean evaluate(String ngram) {
                        return ngram.contains(query);
                    }
                };
                break;

            case EXACT:
                returnVal = new UnaryFunction<String, Boolean>() {

                    @Override
                    public Boolean evaluate(String ngram) {
                        return ngram.equals(query);
                    }
                };
                break;

            case PREFIX:
                returnVal = new UnaryFunction<String, Boolean>() {

                    @Override
                    public Boolean evaluate(String ngram) {
                        return ngram.startsWith(query);
                    }
                };
                break;

            case SUFFIX:
            default:
                returnVal = new UnaryFunction<String, Boolean>() {

                    @Override
                    public Boolean evaluate(String ngram) {
                        return ngram.endsWith(query);
                    }
                };
                break;
        }

        if (matchCase) {
            return returnVal;

        } else {
            // Need to compose two functions, where the first function
            // converts the input string to lower case for easier comparison
            return UnaryFunctionMaker.compose(returnVal,
                    new UnaryFunction<String, String>() {

                        @Override
                        public String evaluate(String ngram) {
                            return ngram.toLowerCase();

                        }
                    });
        }
    }

    @Override
    public String toString() {
        return StringFunctions.concat(
                "Query: ", query,
                " Gram: [", Stringify.intToString(startGram), ",", Stringify.intToString(endGram), "]");
    }
}
